package com.ruoyi.system.service.impl;

import java.lang.reflect.Array;
import java.time.ZoneOffset;
import java.util.*;
import java.util.regex.Pattern;

import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.data.ListDataRefactor;
import com.ruoyi.system.service.TwTweetsPullService;
import com.ruoyi.system.service.TwUserPullService;
import com.twitter.clientlib.model.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.system.mapper.TwitterTwuserMapper;
import com.ruoyi.system.domain.TwitterTwuser;
import com.ruoyi.system.service.ITwitterTwuserService;

/**
 * 推特用户基本信息Service业务层处理
 *
 * @author ruoyi
 * @date 2022-05-31
 */
@Service
public class TwitterTwuserServiceImpl implements ITwitterTwuserService {
    private static final String PATTERN = "(.*nfd.*)|(.*defi.*)|(.*dapp.*)|(.*web3.0.*)|(.*btc.*)|(.*fox.*)|(.*eth.*)";

    @Autowired
    private TwUserPullService twUserPullService;

    @Autowired
    private TwTweetsPullService twTweetsPullService;

    @Autowired
    private TwitterTwuserMapper twitterTwuserMapper;

    /**
     * 启动推特用户抓取功能
     */
    @Override
    public void beginFetch() {
        String username = "TheMoonCarl";
        String username1 = "CryptoCapo_";

        // TODO 重新生成替换，把时间搞成UTC时间存入，写个test，统计一下关键字试试正确不（关键字后面整到字典表或者哪里看比较好？） ===完成
        // TODO 写一个大V的过滤规则（后面存入数据库给管理人员配置）===完成一半
        // TODO（bearer token也写成可配置的，分抓取的和给用户查看的，更新用户的，用户可能想要查看推文发消息用另一个）
        // TODO 单独加入某个大V的信息和更新某个大V的信息(这个逻辑不要搜索其跟随者和关注者)。
        // TODO 完成上面的逻辑（指定大V列表【加个字段，是否指定的大V，即会优先搜索或更新】）
        // TODO 关注者那边的逻辑报空指针，看一下 ===解决，是因为某些用户已经注销，所以数据为空！！

        // TODO 启动，先从库中查询是否有优先要爬取的（已经爬取了做更新操作），获取用户名。
        // TODO 爬取完成要把是否优先爬取字段设置为0，把已经从他去搜索其他人设置为1
        // TODO 如果没有优先要搜索的，则从库中找一个匹配条件最大的大V（没有从他搜索过其他人的0）
        // TODO else，当上诉两种情况都没有，则去搜索最近的包含关键词的推文，匹配一个符合条件的大V，然后开始继续如上的逻辑

        // TODO 看推特官网是否有查看当前应用可剩余的请求次数，如果中途没了请求次数应该做记录？

        // TODO 增加最后一条推文的id，下次更新可以只需要查询少量的推文即可。

        // TODO 引入v1.1，尝试直接用户对用户发送消息。


        // TODO 调试一下为什么query的条数都为0。写一下如果有数据了做更新。==========
        // TODO 把表结构，功能点，拉取思路，后续的优化思路，计划等整理出来。==========
        // TODO 对响应的一些错误进行判断（响应码429评率太高等）和日志记录。
        // TODO 7天推文数，统计一下

//          // TODO 测试一下为什么这个queryNumber会变成0
//        // 根据单个推特用户去不同维度拉取
//        List<TwitterTwuser> twitterTwuserList = this.getUsers(Arrays.asList(username, username1));
//        // 存库
//        this.insertTwitterTwusers(twitterTwuserList);// TODO 字段设置为1

//        // 获取其符合条件的关注者
//        List<TwitterTwuser> totalUsersFollowing = new ArrayList<>();
//        for (TwitterTwuser twuser : twitterTwuserList) {
//            List<TwitterTwuser> followingUsers = this.getFollowingUsers(twuser.getTwUserId(), null);
//            totalUsersFollowing.addAll(followingUsers);
//        }
//        // 符合条件的关注者存库
//        this.insertTwitterTwusers(totalUsersFollowing);


        // 测试批量插入
        List<TwitterTwuser> twuserList = new ArrayList<>(1024);
        Date now = new Date();
        for (int i = 0; i < 1000; i++) {
            TwitterTwuser twuser = new TwitterTwuser();
            twuser.setTwUserId("11111111");
            twuser.setUsername("AestTestTestTest");
            twuser.setName("aesttesttesttest");
            twuser.setCreatedAt(new Date());
            twuser.setDescription("AG: @ProTheDoge Glauber Contessoto The Dogecoin Millionaire *Tweets NFA*\uD83E\uDD32\uD83C\uDFFC\uD83D\uDC8E thedogecoinmillionaire@gmail.com https://t.co/AC6gcQzjhK get Dogecard\uD83D\uDC47\uD83C\uDFFC");
            twuser.setPinnedTweetId("3503819454701920260123");
            twuser.setProfileImageUrl("ftp://pbs.twimg.com/profile_images/1461112627048816646/RJTO7M9W_normal.jpg");
            twuser.setProtected(0L);
            twuser.setFollowersCount(945345345L);
            twuser.setFollowingCount(845345345L);
            twuser.setTweetCount(745345345L);
            twuser.setListedCount(645345345L);
            twuser.setQueryTweetsNumber(555345345L);
            twuser.setTweetsLikeCount(445345345L);
            twuser.setTweetsMostLikeCount(345345345L);
            twuser.setTweetsRetweetCount(245345345L);
            twuser.setTweetsMostRetweetCount(145345345L);
            twuser.setTweetsQuoteCount(645345344L);
            twuser.setTweetsReplyCount(645345343L);
            twuser.setTweetsMostReplyCount(645345342L);
            twuser.setTweetsKeywordNumber(645345341L);
            twuser.setLatestTweetTime(now);
            twuser.setPriorityCrawl(0L);
            twuser.setSearchedOthers(1L);
            twuser.setUpdateTime(now);
            twuser.setCreateTime(now);
            twuserList.add(twuser);
        }
        this.saveTwitterTwusers(twuserList);


    }

    /**
     * 保存推特用户数据；已存在则更新，无则新增
     *
     * @param twitterTwuserList
     */
    public void saveTwitterTwusers(List<TwitterTwuser> twitterTwuserList) {
        if (twitterTwuserList.size() == 0) {
            return;
        }
        // 分出要新增和更新的
        List<TwitterTwuser> updateList = new ArrayList<>(512);
        List<TwitterTwuser> addList = new ArrayList<>(512);
        // TODO 写成使用in方式查询，每次1000条
        twitterTwuserList.stream().forEach(twuser -> {
            Boolean isExistTheUser = this.isTwuserExistByTwUserId(twuser.getTwUserId());
            if (isExistTheUser != null && isExistTheUser == true) {
                updateList.add(twuser);
            } else {
                addList.add(twuser);
            }
        });

        // 批量插入，每次最多1000条
        int addListSize = addList.size();
        if (addListSize > 0) {
            if (addList.size() <= 1000) {
                twitterTwuserMapper.insertBatchTwitterTwuser(addList);
            } else {
                // list平均分割
                List<List<TwitterTwuser>> addListAssignedList = ListDataRefactor.averageAssign(addList, 1000);
                // 分次批量插入
                addListAssignedList.stream().forEach(addUserList -> {
                    twitterTwuserMapper.insertBatchTwitterTwuser(addUserList);
                });
            }
        }

        // 批量更新，每次最多1000条 TODO 优化，本机操作了1000条需要5秒多，且sql又长又臭
        int updateListSize = updateList.size();
        if (updateListSize > 0) {
            if (updateListSize <= 1000) {
                twitterTwuserMapper.updateBatchTwitterTwuser(updateList);
            } else {
                // list平均分割
                List<List<TwitterTwuser>> updateListAssignedList = ListDataRefactor.averageAssign(updateList, 1000);
                // 分次批量更新
                updateListAssignedList.stream().forEach(updateUserList -> {
                    twitterTwuserMapper.updateBatchTwitterTwuser(updateUserList);
                });
            }
        }

        // TODO 获取跟随者的时候，只做新增（得再查一下库，如果存在，则不进行获取用户信息和推文信息去再更新了，后面需要再做定期更新把）
        // TODO 完成根据关键字如何搜索推文，完善逻辑
        // TODO 搬到github的新项目框架上
        // TODO 实现如何用户-用户发送消息
        // TODO 实现如何一键切换bearerToken或获取当前token的剩余次数，进行智能拉取，避免次数不够浪费。
    }

    /**
     * 将推特模型User数据拷贝到本地User对象
     *
     * @param originUser   原始用户对象，推特那边的数据响应对象
     * @param targetTwUser 本地重构的推特用户数据对象
     */
    private void convertTwUserToLocalUser(User originUser, TwitterTwuser targetTwUser) {
        targetTwUser.setTwUserId(originUser.getId());
        targetTwUser.setCreatedAt(Date.from(originUser.getCreatedAt().toInstant()));
        targetTwUser.setDescription(originUser.getDescription());
        targetTwUser.setName(originUser.getName());
        targetTwUser.setUsername(originUser.getUsername());
        targetTwUser.setPinnedTweetId(originUser.getPinnedTweetId());
        targetTwUser.setProfileImageUrl(originUser.getProfileImageUrl().toString());
        targetTwUser.setProtected(originUser.getProtected() == true ? 1L : 0L);
        targetTwUser.setFollowersCount(originUser.getPublicMetrics().getFollowersCount().longValue());
        targetTwUser.setFollowingCount(originUser.getPublicMetrics().getFollowingCount().longValue());
        targetTwUser.setTweetCount(originUser.getPublicMetrics().getTweetCount().longValue());
        targetTwUser.setListedCount(originUser.getPublicMetrics().getListedCount().longValue());
    }

    /**
     * 过滤掉不符合条件的推特用户
     */
    private List<User> filterTwUser(List<User> userList) {
        int followerCount = 3000;
        // TODO 数据库查询过滤规则
        ListIterator<User> iterator = userList.listIterator();
        while (iterator.hasNext()) {
            User twuser = iterator.next();
            // 过滤掉粉丝数不达标的
            if (twuser.getPublicMetrics().getFollowersCount() < followerCount) {
                iterator.remove();
            }
        }
        return userList;
    }

    /**
     * 获取其关注者中符合条件的推特用户
     *
     * @param userId
     * @param paginationToken
     */
    public List<TwitterTwuser> getFollowingUsers(String userId, String paginationToken) {
        List<User> twitterUserList = twUserPullService.fetchFollowingUsers(userId, paginationToken);

        // 过滤一下符合条件的用户
        this.filterTwUser(twitterUserList);

        // 统计用户推文数据并加入列表
        return this.getAndCountUserTweets(twitterUserList);
    }

    /**
     * 获取符合条件的推特用户列表
     *
     * @param usernames 用户名称列表，不超过100个
     */
    public List<TwitterTwuser> getUsers(List<String> usernames) {
        // 抓取用户列表
        MultiUserLookupResponse usersResponse = twUserPullService.fetchUsersByUsernames(usernames);
        List<User> twitterUserList = usersResponse.getData();

        // 过滤掉不符合条件的用户
        this.filterTwUser(twitterUserList);

        // 统计用户推文数据并加入列表
        return this.getAndCountUserTweets(twitterUserList);
    }

    /**
     * 获取该推特用户下能拉取到的推文并统计
     *
     * @param twitterUserList 推特的用户列表
     * @return
     */
    private List<TwitterTwuser> getAndCountUserTweets(List<User> twitterUserList) {
        if (twitterUserList.size() > 0) {
            List<TwitterTwuser> localTwitterUsers = new ArrayList<>(1024);
            // 遍历拉取到的推特用户，统计推文数据
            twitterUserList.stream().forEach(twitterUser -> {
                TwitterTwuser localTwUser = new TwitterTwuser();
                // 拷贝对象基本信息
                this.convertTwUserToLocalUser(twitterUser, localTwUser);
                this.getAndCountUserTweets(localTwUser, twitterUser.getId(), null);
                localTwitterUsers.add(localTwUser);
            });
            return localTwitterUsers;
        } else {
            return new ArrayList<>();
        }
    }

    /**
     * 获取该推特用户下能拉取到的推文并统计 TODO 速率控制(令牌桶可能不适合，会忽略了一些线程，可否用队列？)
     *
     * @param twUser          本地存储对象
     * @param twUserId        推特用户ID
     * @param paginationToken 下一页token，首次查询不用给值
     * @return
     */
    private void getAndCountUserTweets(TwitterTwuser twUser, String twUserId, String paginationToken) {
        // 获取用户的推文（分页）
        GenericTweetsTimelineResponse response = twTweetsPullService.fetchTweetsByUserId(twUserId, paginationToken);
        List<Tweet> tweets = response.getData();
        GenericTweetsTimelineResponseMeta tweetsMetaData = response.getMeta();

        // 统计该推文数据
        Long twUserQueryTweetsNumber = twUser.getQueryTweetsNumber();
        twUser.setQueryTweetsNumber(twUserQueryTweetsNumber == null ? 0 : twUserQueryTweetsNumber + tweetsMetaData.getResultCount());
        if (tweets != null && tweets.size() > 0) {
            tweets.stream().forEach(tweet -> {
                TweetPublicMetrics tweetMetrics = tweet.getPublicMetrics();
                Long tweetsLikeCount = twUser.getTweetsLikeCount();
                Long tweetsMoistLikeCount = twUser.getTweetsMostLikeCount();
                Long tweetsRetweetCount = twUser.getTweetsRetweetCount();
                Long tweetsMostRetweetCount = twUser.getTweetsMostRetweetCount();
                Long tweetsQuoteCount = twUser.getTweetsQuoteCount();
                Long tweetsReplyCount = twUser.getTweetsReplyCount();
                Long tweetsMostReplyCount = twUser.getTweetsMostReplyCount();
                Long tweetsKeywordNumber = twUser.getTweetsKeywordNumber();
                // 全部推文累计like数
                twUser.setTweetsLikeCount(tweetsLikeCount == null ? 0 : (tweetsLikeCount + tweetMetrics.getLikeCount()));
                // 单推文最高like
                twUser.setTweetsMostLikeCount(tweetsMoistLikeCount == null ? 0 :
                        (tweetMetrics.getLikeCount() > tweetsMoistLikeCount ? tweetMetrics.getLikeCount() : tweetsMoistLikeCount));
                // 全部推文累计转发数
                twUser.setTweetsRetweetCount(tweetsRetweetCount == null ? 0 : (tweetsRetweetCount + tweetMetrics.getRetweetCount()));
                // 单tweet最高转发数
                twUser.setTweetsMostRetweetCount(tweetsMostRetweetCount == null ? 0 :
                        (tweetMetrics.getRetweetCount() > tweetsMostRetweetCount ? tweetMetrics.getRetweetCount() : tweetsMostRetweetCount));
                // 全部推文被引用数
                twUser.setTweetsQuoteCount(tweetsQuoteCount == null ? 0 : (tweetsQuoteCount + tweetMetrics.getQuoteCount()));
                // 全部推文评论数
                twUser.setTweetsReplyCount(tweetsLikeCount == null ? 0 : (tweetsReplyCount + tweetMetrics.getReplyCount()));
                // 单tweet最高评论数
                twUser.setTweetsMostReplyCount(tweetsMostReplyCount == null ? 0 :
                        (tweetMetrics.getReplyCount() > tweetsMostReplyCount ? tweetMetrics.getReplyCount() : tweetsMostReplyCount));
                // 最新的推文发布时间
                twUser.setLatestTweetTime(Date.from(tweet.getCreatedAt().toInstant()));

                // 推文中若包含关键字，计数
                String text = tweet.getText().toLowerCase();
                if (Pattern.matches(PATTERN, text)) {
                    twUser.setTweetsKeywordNumber(tweetsKeywordNumber == null ? 0 : tweetsKeywordNumber + 1);
                }
            });
        }


        // 有下一页，则递归调用 TODO 后面有账号记得打开，账号请求次数有限
//        paginationToken = response.getMeta().getNextToken();
//        if(StringUtils.isNotEmpty(paginationToken)){
//            this.getAndCountUserTweets(twUser, twUserId, paginationToken);
//        }
    }

    /**
     * 查询推特用户信息是否存在
     *
     * @param twUserId 推特用户ID
     * @return 存在则为ture，不存在是null
     */
    @Override
    public Boolean isTwuserExistByTwUserId(String twUserId) {
        return twitterTwuserMapper.isTwuserExistByTwUserId(twUserId);
    }

    /**
     * 查询推特用户基本信息
     *
     * @param id 推特用户基本信息主键
     * @return 推特用户基本信息
     */
    @Override
    public TwitterTwuser selectTwitterTwuserById(Long id) {
        return twitterTwuserMapper.selectTwitterTwuserById(id);
    }

    /**
     * 查询推特用户基本信息列表
     *
     * @param twitterTwuser 推特用户基本信息
     * @return 推特用户基本信息
     */
    @Override
    public List<TwitterTwuser> selectTwitterTwuserList(TwitterTwuser twitterTwuser) {
        return twitterTwuserMapper.selectTwitterTwuserList(twitterTwuser);
    }

    /**
     * 新增推特用户基本信息
     *
     * @param twitterTwuser 推特用户基本信息
     * @return 结果
     */
    @Override
    public int insertTwitterTwuser(TwitterTwuser twitterTwuser) {
        twitterTwuser.setCreateTime(DateUtils.getNowDate());
        return twitterTwuserMapper.insertTwitterTwuser(twitterTwuser);
    }

    /**
     * 修改推特用户基本信息
     *
     * @param twitterTwuser 推特用户基本信息
     * @return 结果
     */
    @Override
    public int updateTwitterTwuser(TwitterTwuser twitterTwuser) {
        twitterTwuser.setUpdateTime(DateUtils.getNowDate());
        return twitterTwuserMapper.updateTwitterTwuser(twitterTwuser);
    }

    /**
     * 批量删除推特用户基本信息
     *
     * @param ids 需要删除的推特用户基本信息主键
     * @return 结果
     */
    @Override
    public int deleteTwitterTwuserByIds(Long[] ids) {
        return twitterTwuserMapper.deleteTwitterTwuserByIds(ids);
    }

    /**
     * 删除推特用户基本信息信息
     *
     * @param id 推特用户基本信息主键
     * @return 结果
     */
    @Override
    public int deleteTwitterTwuserById(Long id) {
        return twitterTwuserMapper.deleteTwitterTwuserById(id);
    }

}
